"""
数据库序列化和反序列化工具模块

提供高效的数据序列化功能，支持数据库记录、索引键值、字段值等的序列化
"""

import struct
from msilib.schema import tables
from typing import Any, Dict, List, Optional, Tuple, Union
from enum import Enum
from datetime import datetime, date
import json
import os

from src.utils.parser import Parser
from src.utils.type_converter import TypeConverter


class SerializationType(Enum):
    """序列化类型枚举"""
    NULL = 0
    BOOLEAN = 1
    INTEGER = 2
    FLOAT = 3
    STRING = 4
    DATE = 5
    BYTES = 6


class Serializer:
    """统一序列化器
    
    整合了原有的CompactSerializer、StructSchema、StructRecordSerializer的功能，
    以及Field和Table中的序列化方法，提供基于struct的高效序列化实现。
    """
    
    @staticmethod
    def key_to_uid(key: Any, field_type) -> int:
        """将键值转换为UID
        
        Args:
            key: 键值
            field_type: 字段类型
            
        Returns:
            生成的UID
        """
        from catalog.field import FieldType

        uid = 0
        if field_type == FieldType.INTEGER:
            uid =  int(key)
        elif field_type == FieldType.FLOAT:
            uid = Parser.float_to_uid(float(key))
        elif field_type == FieldType.BOOLEAN:
            uid = 1 if key == True else 0
        elif field_type == FieldType.VARCHAR or field_type == FieldType.DATE:
            # 使用取模操作限制字符串生成的索引键大小
            # 使用2^31-1作为模数，确保生成的键值在有符号32位整数范围内
            uid = Parser.str_to_uid(key) % 2147483647  # 2^31-1
        return uid
    
    @staticmethod
    def serialize_int_key(value: int) -> bytes:
        """序列化整数键（用于B+树索引）"""
        return value.to_bytes(8, byteorder='little', signed=True)
    
    @staticmethod
    def deserialize_int_key(data: bytes) -> int:
        """反序列化整数键"""
        return int.from_bytes(data, byteorder='little', signed=True)
    
    @staticmethod
    def serialize_string_key(value: str, max_length: int = 255) -> bytes:
        """序列化字符串键（定长）"""
        encoded = value.encode('utf-8')[:max_length]
        return encoded.ljust(max_length, b'\x00')
    
    @staticmethod
    def deserialize_string_key(data: bytes) -> str:
        """反序列化字符串键"""
        return data.rstrip(b'\x00').decode('utf-8')
    
    '''
    field类
    '''
    @staticmethod
    def serialize_field_meta(field) -> bytes:
        name_raw = Parser.string_to_bytes(field.name)
        type_raw = Parser.string_to_bytes(field.field_type.value)
        # 将索引根页号作为 index_uid 写入（没有则为 0）
        index_uid_value = 0
        if field.btree_index is not None:
            # 优先确保根页已分配
            index_uid_value = int(field.btree_index.boot_uid)
        index_uid_raw = Parser.long_to_bytes(index_uid_value)
        return name_raw + type_raw + index_uid_raw
    @staticmethod
    def deserialize_field_meta(raw: bytes) -> Any:
        # 解析字段名
        name, pos = Parser.parse_string(raw)
        # 解析类型名
        type_name, comsume = Parser.parse_string(raw[pos:])
        pos +=comsume
        # 解析索引UID（根页号）
        index_uid = Parser.parse_long(raw[pos:pos + 8]) if len(raw) >= pos + 8 else 0

        return name,type_name,index_uid,TypeConverter._normalize_field_type(type_name)

    @staticmethod
    def _normalize_type(field_type: Any) :
        from catalog.field import FieldType

        """将多种表示标准化为 FieldType

        将不同形式的类型表示（字符串、枚举等）转换为标准的FieldType枚举

        Args:
            field_type: 输入的字段类型表示

        Returns:
            标准化的FieldType枚举值

        Raises:
            ValueError: 当类型不受支持时抛出异常
        """
        if isinstance(field_type, FieldType):
            return field_type
        if isinstance(field_type, str):
            s = field_type.strip().lower()
            if s in ("int", "int32", "int64", "integer"):
                return FieldType.INTEGER
            if s in ("varchar", "string", "text"):
                return FieldType.VARCHAR
            if s in ("float", "double", "real"):
                return FieldType.FLOAT
            if s in ("bool", "boolean"):
                return FieldType.BOOLEAN
            if s in ("date", "datetime", "timestamp"):
                return FieldType.DATE
        raise ValueError(f"Unsupported field type: {field_type}")
    @staticmethod
    def serialize_value(value: Any, field_type) -> bytes:
        """将单个字段值序列化为字节
        
        根据字段类型将Python值转换为二进制表示
        
        Args:
            value: 要序列化的值
            field_type: 字段类型
            
        Returns:
            序列化后的字节数组
        """
        from catalog.field import FieldType
        
        if value is None:
            # 统一以长度0表示None（变长类型）
            if field_type in (FieldType.VARCHAR, FieldType.DATE):
                return Parser.string_to_bytes("")
            # 固定长类型用全0
            if field_type == FieldType.INTEGER:
                return struct.pack("<q", 0)
            if field_type == FieldType.FLOAT:
                return struct.pack("<d", 0.0)
            if field_type == FieldType.BOOLEAN:
                return b"\x00"
        
        if field_type == FieldType.INTEGER:
            return struct.pack("<q", int(value))
        if field_type == FieldType.FLOAT:
            return struct.pack("<d", float(value))
        if field_type == FieldType.BOOLEAN:
            return b"\x01" if bool(value) else b"\x00"
        if field_type in (FieldType.VARCHAR, FieldType.DATE):
            return Parser.string_to_bytes(str(value))
        # 默认回退为字符串
        return Parser.string_to_bytes(str(value))
    
    @staticmethod
    def deserialize_value(raw: bytes, field_type) -> Tuple[Any, int]:
        """从字节解析单个字段值
        
        根据字段类型从二进制数据解析出Python值
        
        Args:
            raw: 要解析的字节数组
            field_type: 字段类型
            
        Returns:
            元组(value, consumed_bytes)，包含解析出的值和消耗的字节数
        """
        from catalog.field import FieldType
        
        if field_type == FieldType.INTEGER:
            if len(raw) < 8:
                return 0, 0
            return struct.unpack("<q", raw[:8])[0], 8
        if field_type == FieldType.FLOAT:
            if len(raw) < 8:
                return 0.0, 0
            return struct.unpack("<d", raw[:8])[0], 8
        if field_type == FieldType.BOOLEAN:
            if not raw:
                return False, 0
            return (raw[0] != 0), 1
        if field_type in (FieldType.VARCHAR, FieldType.DATE):
            return Parser.parse_string(raw)
        # 默认当做字符串
        s, consumed = Parser.parse_string(raw)
        return s, consumed
    
    '''
    table类
    '''
    @staticmethod
    def serialize_table_meta(table) -> bytes:
        """持久化表数据

                将表的元数据序列化并写入存储系统

                Args:
                    xid: 事务ID

                Returns:
                    持久化后的表对象（self）
                """
        # 序列化表名
        name_bytes = Parser.string_to_bytes(table.name)

        # 序列化下一个表UID
        next_uid_bytes = Parser.long_to_bytes(table.next_uid)

        # 序列化字段UID列表
        field_uids = b''.join([Parser.long_to_bytes(f.uid) for f in table.fields])

        # 组合所有数据
        raw = name_bytes + next_uid_bytes + field_uids

        return raw

    def deserialize_table_meta(raw: bytes):
        position = 0
        # 解析表名
        name, consumed = Parser.parse_string(raw)
        position += consumed

        # 解析下一个表的UID
        next_uid = Parser.parse_long(raw[position:position + 8])
        position += 8

        # 解析字段UID列表
        uids=[]
        while position + 8 <= len(raw):
            field_uid = Parser.parse_long(raw[position:position + 8])
            position += 8
            uids.append(field_uid)
        return name,next_uid,uids

    @staticmethod
    def entry_to_raw(entry: Dict[str, Any], fields) -> bytes:
        """将记录转换为字节数组
        
        将字段值字典序列化为二进制格式
        
        Args:
            entry: 字段值字典，键为字段名，值为字段值
            fields: 字段列表
            
        Returns:
            序列化后的字节数组
        """
        raw = b''
        for field in fields:
            value = entry.get(field.name)
            raw += Serializer.serialize_value(value, field.field_type)
        return raw
    
    @staticmethod
    def parse_entry(raw: bytes, fields) -> Dict[str, Any]:
        """解析记录
        
        从二进制数据解析出字段值字典
        
        Args:
            raw: 记录的二进制数据
            fields: 字段列表
            
        Returns:
            解析后的字段值字典，键为字段名，值为字段值
        """
        entry = {}
        position = 0
        for field in fields:
            value, consumed = Serializer.deserialize_value(raw[position:], field.field_type)
            entry[field.name] = value
            position += consumed
        return entry
    
    # 从Booter类整合的序列化方法
    @staticmethod
    def update_booter_data(path: str, data: bytes, suffix: str, tmp_suffix: str) -> None:
        """更新booter数据
        
        Args:
            path: 文件路径
            data: 要写入的数据
            suffix: 文件后缀
            tmp_suffix: 临时文件后缀
        """
        if len(data) != 8:
            raise ValueError("Data must be 8 bytes long")
        
        file_path = path + suffix
        tmp_path = path + tmp_suffix
        
        # 写入临时文件
        with open(tmp_path, 'wb') as f:
            f.write(data)
        
        # 原子性替换
        if os.name == 'nt':  # Windows
            if os.path.exists(file_path):
                os.remove(file_path)
            os.rename(tmp_path, file_path)
        else:  # Unix-like
            os.rename(tmp_path, file_path)
    
    # 原有的StructSchema功能
    def __init__(self, schema: Optional[List[Tuple[str, str]]] = None, endian: str = '<'):
        """初始化序列化器
        
        Args:
            schema: 可选的结构化模式定义，格式为[(字段名, 类型规范), ...]
            endian: 字节序，'<'表示小端，'>'表示大端
        """
        self.endian = endian
        self.schema = schema
        self._fmt = self._build_format() if schema else None
    
    def _build_format(self) -> str:
        """根据schema构建struct格式字符串"""
        if not self.schema:
            return self.endian
            
        codes = []
        for _, t in self.schema:
            if t == 'int64':
                codes.append('q')
            elif t == 'uint64':
                codes.append('Q')
            elif t == 'int32':
                codes.append('i')
            elif t == 'uint32':
                codes.append('I')
            elif t == 'float64':
                codes.append('d')
            elif t == 'bool':
                codes.append('?')
            elif t.startswith('str:'):
                n = int(t.split(':', 1)[1])
                codes.append(f'{n}s')
            elif t.startswith('bytes:'):
                n = int(t.split(':', 1)[1])
                codes.append(f'{n}s')
            else:
                raise ValueError(f'Unsupported struct type: {t}')
        return self.endian + ''.join(codes)
    
    @property
    def format(self) -> str:
        """获取struct格式字符串"""
        return self._fmt if self._fmt else self.endian
    
    def serialize_record(self, record: Dict[str, Any]) -> bytes:
        """序列化记录
        
        Args:
            record: 要序列化的记录字典
            
        Returns:
            序列化后的字节数组
        """
        if not self.schema:
            raise ValueError("Schema must be provided for record serialization")
            
        values: List[Any] = []
        for name, t in self.schema:
            v = record.get(name)
            if t.startswith('str:'):
                n = int(t.split(':', 1)[1])
                b = ('' if v is None else str(v)).encode('utf-8')[:n]
                values.append(b.ljust(n, b'\x00'))
            elif t.startswith('bytes:'):
                n = int(t.split(':', 1)[1])
                b = (b'' if v is None else bytes(v))[:n]
                values.append(b.ljust(n, b'\x00'))
            else:
                # 基础标量按原样传给 struct
                values.append(v if v is not None else 0)
        return struct.pack(self.format, *values)
    
    def deserialize_record(self, data: bytes) -> Dict[str, Any]:
        """反序列化记录
        
        Args:
            data: 要反序列化的字节数组
            
        Returns:
            反序列化后的记录字典
        """
        if not self.schema:
            raise ValueError("Schema must be provided for record deserialization")
            
        unpacked = struct.unpack(self.format, data)
        out: Dict[str, Any] = {}
        for (name, t), v in zip(self.schema, unpacked):
            if isinstance(v, (bytes, bytearray)) and (t.startswith('str:') or t.startswith('bytes:')):
                if t.startswith('str:'):
                    out[name] = bytes(v).rstrip(b'\x00').decode('utf-8', errors='ignore')
                else:
                    out[name] = bytes(v)
            else:
                out[name] = v
        return out


# 便捷函数
def create_serializer(schema: Optional[List[Tuple[str, str]]] = None, endian: str = '<') -> Serializer:
    """创建序列化器的便捷函数
    
    Args:
        schema: 可选的结构化模式定义
        endian: 字节序
        
    Returns:
        Serializer实例
    """
    return Serializer(schema, endian)


def serialize_record(record: Dict[str, Any], schema: List[Tuple[str, str]], endian: str = '<') -> bytes:
    """序列化记录的便捷函数
    
    Args:
        record: 要序列化的记录
        schema: 结构化模式定义
        endian: 字节序
        
    Returns:
        序列化后的字节数组
    """
    return Serializer(schema, endian).serialize_record(record)


def deserialize_record(data: bytes, schema: List[Tuple[str, str]], endian: str = '<') -> Dict[str, Any]:
    """反序列化记录的便捷函数
    
    Args:
        data: 要反序列化的字节数组
        schema: 结构化模式定义
        endian: 字节序
        
    Returns:
        反序列化后的记录字典
    """
    return Serializer(schema, endian).deserialize_record(data)